/*
 * ﻿Copyright (C) 2012-2013 NewMain Softech
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package com.newmainsoftech.ant.types;

import java.io.File;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.apache.tools.ant.types.Commandline.Argument;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;

import com.newmainsoftech.ant.ProjectComponentContract;

/**
 * Extracted methods' signature of {@link Argument}
 * 
 * @author <a href="mailto:artymt@gmail.com">Arata Y.</a>
 */
public interface ArgumentContract extends ProjectComponentContract {
	/**
	 * Enumerate methods of {@link Argument} that set value to parts member field.
	 * @author <a href="mailto:artymt@gmail.com">Arata Yamamoto</a>
	 */
	public static enum MethodSetPartsField {
		SetValue( new Class<?>[]{ String.class}), 
		SetLine( new Class<?>[]{ String.class}), 
		SetPath( new Class<?>[]{ Path.class}), 
		SetPathref( new Class<?>[]{ Reference.class}), 
		SetFile( new Class<?>[]{ File.class});
		
		private final List<Class<?>> argTypeList;
			public Class<?>[] getArgTypes() {
				return argTypeList.toArray( new Class<?>[]{});
			}
		MethodSetPartsField( Class<?>[] argTypes) {
			this.argTypeList = Collections.unmodifiableList( Arrays.asList( argTypes));
		}
		
		/**
		 * Get corresponding abstract method defined {@link ArgumentContract} interface.
		 * Note: Method object returned by this and {@link #getArgumentMethod()} are not 
		 * equals because {@link ArgumentContract} was created by extracting methods' signatures 
		 * from Argument class due to Argument class doesn't have interface defined for it, not 
		 * other way around. 
		 * @return method in ArgumentContract interface
		 */
		public Method getArgumentContractMethod() {
			String thisStr = this.name();
			String methodName = thisStr.substring( 0, 1).toLowerCase() + thisStr.substring( 1);
			try {
				Method method = ArgumentContract.class.getDeclaredMethod( methodName, this.getArgTypes());
				return method;
			}
			catch( NoSuchMethodException exception) {
				throw new UnsupportedOperationException(
						String.format(
								"%1$s method with arguments of types %2$s was not found declared in %3$s. " 
								+ "(Please report this because %4$s most likely requires to be updated.)",
								methodName, 
								Arrays.toString( this.getArgTypes()), 
								ArgumentContract.class.getName(), 
								this.getClass().toString()
								),
						exception
						);
			}
		}
		
		/**
		 * Get corresponding method implemented in {@link Argument} class.
		 * Note: Method object returned by this and {@link #getArgumentContractMethod()} are not 
		 * equals because {@link ArgumentContract} was created by extracting methods' signatures 
		 * from Argument class due to Argument class doesn't have interface defined for it, not 
		 * other way around. 
		 * @return method in Argument class
		 */
		public Method getArgumentMethod() {
			String thisStr = this.name();
			String methodName = thisStr.substring( 0, 1).toLowerCase() + thisStr.substring( 1);
			try {
				Method method = Argument.class.getDeclaredMethod( methodName, this.getArgTypes());
				return method;
			}
			catch( NoSuchMethodException exception) {
				throw new UnsupportedOperationException(
						String.format(
								"%1$s method with arguments of types %2$s was not found declared in %3$s. " 
								+ "(Please report this because %4$s and %5$s most likely require to be " 
								+ "updated.)",
								methodName, 
								Arrays.toString( this.getArgTypes()), 
								Argument.class.getName(), 
								ArgumentContract.class.toString(),
								this.getClass().toString()
								),
						exception
						);
			}
		}
	}
	/** @see Argument#setValue(String) */
	public void setValue( String value);
	/** @see Argument#setLine(String) */
	public void setLine( String line);
	/** @see Argument#setPath(Path) */
	public void setPath( Path value);
	/** @see Argument#setPathref(Reference) */
	public void setPathref( Reference value);
	/** @see Argument#setFile(File) */
	public void setFile( File value);
	/** @see Argument#setPrefix(String) */
	public void setPrefix( String prefix);
	/** @see Argument#setSuffix(String) */
	public void setSuffix( String suffix);
	/** @see Argument#getParts() */
	public String[] getParts();
}